package gui;

import game.Game;

import javax.annotation.PreDestroy;
import javax.swing.*;

import java.awt.*;
import java.awt.image.*;

/**
 * @author Ryan Hill
 */
public class GUI extends JFrame implements Runnable
{
	//The serial version uid for the application
	private static final long serialVersionUID = 3648112661186903330L;
	//Average frame rate of the GUI
	private final int rate = 15; 
	//Actual frame rate
	private double frameRate;
	//Current game time; used in calculation for duration of one frame
	private long time;
	//The game that is running in the window
	private Game game;
	//background color of game
	private Color bgColor;
	//determine whether or not to show the FPS
	private boolean displayFPS;
	//Singleton data for the GUI
	private static GUI frame;
	//Graphics Device for the Full Screen Exclusive Mode
	private GraphicsDevice gd;
	
	public static void main(String[] args)
	{
		(new Thread(frame = new GUI())).start();
	}

	
	/**
	 * GUI()
	 * 
	 * Constructor for the GUI class
	 * Since GUI is a singleton class the constructor is
	 * a private method to limit its use too one class
	 */
	private GUI()
	{
		 initScreen();
		 
		 time = System.currentTimeMillis();
		 game = new Game();
		 
		 frameRate = 0;
		 
		 bgColor = Color.black;
		 
		 displayFPS = true;
	
		 this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
		 this.addKeyListener(new GUIKeyListener(game.getUser()));
		 this.addMouseMotionListener(new GUIMouseMotionListener(game.getUser()));
		 this.addMouseListener(new GUIMouseListener(game.getUser()));
		 this.addMouseWheelListener(new GUIMouseWheelListener());
	}
	
	/**
	 * void restoreScreen()
	 * 
	 * As the program exits, call restore screen to dispose
	 * the GUI's graphics
	 */
	@PreDestroy
	public void restoreScreen()
	{
		JFrame f = (JFrame)gd.getFullScreenWindow();
		
		try 
		{
			if(f != null)
				f.dispose();
		}
		catch(IllegalArgumentException err){}
		
		gd.setFullScreenWindow(null);
	}
	
	/**
	 * void initScreen()
	 * 
	 * Initializes the GUI's GraphicsEnvironment, sets it to fullscreen mode
	 * and creates the double buffer
	 */
	private void initScreen()
	{
		GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
		gd = graphicsEnvironment.getDefaultScreenDevice();
		
		this.setUndecorated(true);
		this.setIgnoreRepaint(true);
		try {
			if (gd.isFullScreenSupported()) 
			{
				gd.setFullScreenWindow(this);
				gd.setDisplayMode(new DisplayMode(1024, 768, 32, DisplayMode.REFRESH_RATE_UNKNOWN));
		    }
			 
			this.createBufferStrategy(2);
		} 
		catch(Exception err)
		{
			gd.setDisplayMode(gd.getDisplayMode());
			gd.setFullScreenWindow(null);
		}	 
	}

	@Override
	public void run()
	{
		while(true)
		{
			double duration = (System.currentTimeMillis() - time) / 1000.0;
			game.update(duration);
			
			frameRate = 1 / duration;
			
			render();
			
			time = System.currentTimeMillis();
			
			try
			{
				Thread.sleep(rate);
			}
			catch(InterruptedException err)
			{
				return;
			}
		}
	}
	
	/**
	 * Renders the graphics on the screen
	 */
	private void render()
	{
		try
		{
			BufferStrategy bufferStrategy = this.getBufferStrategy();
			
			Graphics2D g2d = (Graphics2D)bufferStrategy.getDrawGraphics();
			
			g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
			
			g2d.setColor(bgColor);
			g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
			
			if(displayFPS)
			{
				g2d.setColor(Color.white);
				g2d.drawString("Expected FPS: " + Math.round(getExpectedRate()) + " FPS: " + Math.round(frameRate), 50, 50);
			}
			
			game.render(g2d);
			
			g2d.dispose();

			if(!bufferStrategy.contentsLost())
			{
				bufferStrategy.show();
			}
		}
		catch (IllegalStateException err) {}
	}
	
	public void setBGColor(Color c)
	{
		bgColor = c;
	}
	
	public void setDisplayFPS(boolean b)
	{
		displayFPS = b;
	}
	
	/**
	 * Sets the cursor image for the GUI
	 * 
	 * @param img image that the cursor is going to take on
	 */
	public void setCursor(Image img)
	{
		Toolkit t = Toolkit.getDefaultToolkit();
		
		GUI.getInstance().setCursor(t.createCustomCursor(img, new Point(0, 0), "cursor"));
	}
	
	/**
	 * @return the expected fps for the application
	 */
	public double getExpectedRate()
	{
		return 1000.0/rate;
	}
	
	/**
	 * @return the actual fps for the application
	 */
	public double getActualRate()
	{
		return frameRate;
	}

	/**
	 * @return the singleton for GUI
	 */
	public static GUI getInstance()
	{
		return frame;
	}
}
